package TDALista;


/**
 * @author La revancha
 * @version 71
 */
public class TLista {

    /**
     * Separador utilizado entre elemento y elemento al imprimir la lista
     */
    public static final String SEPARADOR_ELEMENTOS_IMPRESOS = "-";
    public TElemento primero;

    /**
     * Constructor por defecto
     */
    public TLista() {
    }

    /**
     * Obtiene el primer elemento de la lista
     *
     * @return el primer elemento de tipo TElemento de la lista
     */
    public TElemento obtenerPrimero() {
        return primero;
    }

    /**
     * Obtiene el ultimo elemento de la lista
     *
     * @return el elemento que se encuentra al final de la lista
     */
    public TElemento obtenerUltimo() {
        if (esVacia()) {
            return null;
        }

        TElemento actual = primero;

        while (actual.siguiente != null) {
            actual = actual.siguiente;
        }

        return actual;
    }

    /**
     * Insersion al principio de una lista
     *
     * @param unElemento
     *            Elemento a insertar
     * @return true si pudo insertarlo
     */
    public boolean insertarAlPrincipio(TElemento unElemento) {
        if (unElemento == null) {
            return false;
        }

        unElemento.siguiente = primero;
        primero = unElemento;

        return true;
    }

    /**
     * Metodo encargado de insertar un elemento al final de la lista.
     *
     * @param unElemento
     *            El elemento que queremos insertar
     * @return true si se pudo insertarlo
     */
    public boolean insertarAlFinal(TElemento unElemento) {
        if (unElemento == null) {
            return false;
        }

        if (esVacia()) {
            primero = unElemento;
        } else {
            obtenerUltimo().siguiente = unElemento;
        }

        unElemento.siguiente = null;

        return true;
    }

    /**
     * Verificar si la lista esta vacia
     *
     * @return true si la lista esta vacia o false en caso contrario
     */
    public boolean esVacia() {
        return primero == null;
    }

    /**
     * Verifica si el el elemento UnElemento se encuentra en la lista
     *
     * @param unElemento
     *            elemento que estamos buscando
     * @return true si el elemento se encuentra en la lista o false en caso
     *         contrario
     */
    public boolean existeElemento(TElemento unElemento) {
        return unElemento != null
                && buscarEtiqueta(unElemento.etiqueta) != null;
    }

    /**
     * Busca el elemento identificado por unaEtiqueta en la lista, y de
     * encontrarlo lo retorna
     *
     * @param unaEtiqueta
     *            Etiqueta asociada al elemento a buscar
     * @return el elemento identificado por unaEtiqueta, de no encontrarlo
     *         retorna null
     */
    @SuppressWarnings("rawtypes")
	public TElemento buscarEtiqueta(Comparable unaEtiqueta) {
        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            if (actual.etiqueta.equals(unaEtiqueta)) {
                return actual;
            }
        }

        return null;
    }

    /**
     * Elimina el elemento unElemento de la lista
     *
     * @param unElemento
     *            Elemento que se desea eliminar
     * @return false si no elimino el elemento, o true en caso de eliminarlo
     */
    public boolean eliminar(TElemento unElemento) {
        if ((esVacia()) || (unElemento == null)) {
            return false;
        }

        if (unElemento.equals(primero)) {
            primero = primero.siguiente;
            return true;
        }

        for (TElemento actual = primero.siguiente, anterior = primero; actual != null; actual = actual.siguiente) {
            if (actual.equals(unElemento)) {
                anterior.siguiente = actual.siguiente;
                return true;
            }

            anterior = actual;
        }

        return false;
    }

    /**
     * Elimina el elemento identificado por unaEtiqueta de la lista
     *
     * @param unaEtiqueta
     *            Etiqueta asociada al elemento que se desea eliminar
     * @return true en caso de eliminarlo o false en caso contrario
     */
    @SuppressWarnings("rawtypes")
	public boolean eliminarEtiqueta(Comparable unaEtiqueta) {

        return eliminar(buscarEtiqueta(unaEtiqueta));

    }

    /**
     * Obtiene la cantidad de elementos de la lista
     *
     * @return un entero representando la cantidad de elementos de la lista
     */
    public int obtenerCantidadElementos() {
        int result = 0;
        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            result++;
        }
        return result;
    }

    /**
     * Inserta el elemento unElemento en orden (de manera ascendente)  en la lista
     *
     * @param unElemento
     *            Elemento a insertar
     * @return true si se logro la insercion
     */
    @SuppressWarnings("unchecked")
	public boolean insertarOrdenado(TElemento unElemento) {
        if (esVacia()
                || primero.etiqueta.compareTo(unElemento.etiqueta) >= 0) {
            return insertarAlPrincipio(unElemento);
        }

        TElemento anterior = primero;
        for (TElemento actual = primero.siguiente; actual != null; actual = actual.siguiente) {
            if (actual.etiqueta.compareTo(unElemento.etiqueta) >= 0) {
                break;
            }
            anterior = actual;
        }

        unElemento.siguiente = anterior.siguiente;
        anterior.siguiente = unElemento;

        return true;
    }

    /**
     * Elimina todos los elementos de la lista del tipo TLista
     */
    public void vaciar() {
        primero = null;
    }

    /**
     * Invierte los elementos de la lista
     *
     * @return una lista del tipo Tlista con los elementos invertidos
     */
    public TLista invertir() {
        TLista lista = new TLista();
        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            lista.insertarAlPrincipio(actual.copia());
        }

        return lista;
    }

    /**
     * Busca el elemento anterior a unElemento
     *
     * @param unElemento
     *            Elemento al cual se le quiere encontrar su anterior
     * @return el elemento del tipo TElemento anterior a un Elemento, si este existe
     */
    public TElemento anterior(TElemento unElemento) {
        if (unElemento == null || unElemento.equals(primero)) {
            return null;
        }

        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            if (unElemento.equals(actual.siguiente)) {
                return actual;
            }
        }

        return null;
    }

    /**
     * Busca el elemento anterior al elemento identificado por unaEtiqueta
     *
     * @param unaEtiqueta
     *            Etiqueta asociada al elemento que se quiere encontrar su
     *            anterior
     * @return el elemento del tipo TElemento anterior al elemento identificado por unaEtiqueta
     */
    @SuppressWarnings("rawtypes")
	public TElemento anterior(Comparable unaEtiqueta) {
        if (esVacia() || unaEtiqueta == null || unaEtiqueta.equals(primero.etiqueta)) {
            return null;
        }

        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            if (actual.siguiente != null && unaEtiqueta.equals(actual.siguiente.etiqueta)) {
                return actual;
            }
        }

        return null;
    }

    /**
     * Construye un arreglo de Strings con las etiquetas de los elementos de la
     * lista
     *
     * @return un vector de strings
     */
    @SuppressWarnings("rawtypes")
	public Comparable[] etiquetasToArray() {
        Comparable[] result = new Comparable[obtenerCantidadElementos()];

        TElemento actual = primero;
        for (int i = 0; i < result.length; i++) {
            result[i] = actual.etiqueta;
            actual = actual.siguiente;
        }

        return result;
    }

    /**
     * Retorna en un solo string las etiquetas de todos los elementos de la
     * lista, separados por la constante SEPARADOR_ELEMENTOS_IMPRESOS
     *
     * @return un String con las etiquetas de los elementos de la lista
     */
    public String imprimirEtiquetas() {
        if (esVacia()) {
            return "";
        }

        String result = primero.imprimirEtiqueta();
        for (TElemento actual = primero.siguiente; actual != null; actual = actual.siguiente) {
            result += SEPARADOR_ELEMENTOS_IMPRESOS + actual.etiqueta;
        }

        return result;
    }

    /**
     * Ordena los elementos de la lista en forma ascendente
     *
     * @return una nueva lista del tipo TLista ordenada en forma ascendente
     */
    public TLista ordenar() {
        TLista lista = new TLista();
        for (TElemento actual = primero; actual != null; actual = actual.siguiente) {
            lista.insertarOrdenado(actual.copia());
        }
        return lista;
    }

    /**
     * Elimina el primer elemento de la lista y lo devuelve
     *
     * @return el primer elemento de tipo TElemento de la lista
     */
    public TElemento quitarPrimero() {
        if (esVacia()) {
            return null;
        }

        TElemento result = primero;
        primero = primero.siguiente;
        return result;
    }
}
